package com.karumi.dexter.uitool;
/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import com.karumi.dexter.ResourceTable;

import ohos.agp.colors.RgbColor;
import ohos.agp.components.BaseItemProvider;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.Image;
import ohos.agp.components.InputAttribute;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.ListContainer;
import ohos.agp.components.Text;
import ohos.agp.components.TextField;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.Paint;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.agp.utils.TextAlignment;
import ohos.agp.window.dialog.CommonDialog;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Locale;

/**
 * 自定义弹框
 */
public class DexterCustomDialog extends CommonDialog
        implements Component.ClickedListener, ListContainerAdapter.InternalListCallback {
    private static final String TAG = DexterCustomDialog.class.getSimpleName();
    private Text tvMinMax;
    private TextField input;
    private ListContainer listContainer;
    private OnPositiveListener listener;
    final Builder builder;
    private Text tvTitle;
    private Image ivLogo;
    private DirectionalLayout titleFrame;
    private Text tvContent;
    private Button btnPositive;
    private Button btnNeutral;
    private Button btnNegative;
    private boolean textAllCaps;

    private DexterCustomDialog(Builder builder) {
        super(builder.getContext());
        this.builder = builder;

        ComponentContainer rootLayout =
                (ComponentContainer)
                        LayoutScatter.getInstance(builder.getContext()).parse(getInflateLayout(builder), null, false);

        setSize(WindowUtil.getWindowWdith(builder.getContext()) * 2 / 3, ComponentContainer.LayoutConfig.MATCH_CONTENT);
        prepareDialogView(rootLayout, builder);
        setContentCustomComponent(rootLayout);
    }

    @Override
    protected void onCreate() {
        super.onCreate();
    }

    private void prepareDialogView(ComponentContainer rootLayout, Builder builder) {
        initView(rootLayout);
        initViewData(rootLayout);
        btnListenerMethod();

        if (null != builder.neutralText && btnNeutral != null) {
            btnNeutral.setFont(builder.mediumFont);
            String text = builder.neutralText.toString() != null ? builder.neutralText.toString() : "";
            if (textAllCaps && !text.equals("")) {
                text = text.toUpperCase(Locale.ROOT);
            }
            btnNeutral.setText(text);
            btnNeutral.setTextColor(
                    new Color(ResTUtil.getColor(builder.getContext(), ResourceTable.Color_colorAccent)));
            btnNeutral.setTextAlignment(TextAlignment.CENTER);
            btnNeutral.setTag(DialogTAction.NEUTRAL);
            btnNeutral.setClickedListener(this);
        }

        if (listContainer != null && builder.baseItemProvider == null) {
            builder.baseItemProvider = new ListContainerAdapter(this, builder.getContext());
        }

        invalidateList();
        setupInputDialog(builder, rootLayout);
    }

    private void initView(ComponentContainer rootLayout) {
        // Retrieve references to views
        tvTitle = UiTUtil.getComponent(rootLayout, ResourceTable.Id_title);
        ivLogo = UiTUtil.getComponent(rootLayout, ResourceTable.Id_icon);
        titleFrame = UiTUtil.getComponent(rootLayout, ResourceTable.Id_titleFrame);
        tvContent = UiTUtil.getComponent(rootLayout, ResourceTable.Id_content);
        listContainer = UiTUtil.getComponent(rootLayout, ResourceTable.Id_listContainer);

        // Button views initially used by checkIfStackingNeeded()
        btnPositive = UiTUtil.getComponent(rootLayout, ResourceTable.Id_btnPositive);
        btnNeutral = UiTUtil.getComponent(rootLayout, ResourceTable.Id_btnNeutral);
        btnNegative = UiTUtil.getComponent(rootLayout, ResourceTable.Id_btnNegative);

        // Don't allow the submit button to not be shown for input dialogs
        if (builder.inputCallback != null && builder.positiveText == null) {
            builder.positiveText = ResTUtil.getString(builder.getContext(), ResourceTable.String_dexter_ok);
        }

        // Set up the initial visibility of action buttons based on whether or not text was set
        if (btnPositive != null) {
            btnPositive.setVisibility(builder.positiveText != null ? Component.VISIBLE : Component.HIDE);
        }
        if (btnNeutral != null) {
            btnNeutral.setVisibility(builder.neutralText != null ? Component.VISIBLE : Component.HIDE);
        }
        if (btnNegative != null) {
            btnNegative.setVisibility(builder.negativeText != null ? Component.VISIBLE : Component.HIDE);
        }

        if (ivLogo != null) {
            ivLogo.setVisibility(Component.VISIBLE);
            ivLogo.setImageElement(builder.icon);
        }
    }

    private void initViewData(ComponentContainer rootLayout) {
        Paint paint = new Paint();
        paint.setColor(new Color(Color.GRAY.getValue()));
        rootLayout.invalidate();

        // Setup title and title frame
        if (tvTitle != null) {
            tvTitle.setFont(builder.mediumFont);
            tvTitle.setTextColor(
                    new Color(ResTUtil.getColor(builder.getContext(), ResourceTable.Color_material_black)));
            tvTitle.setTextAlignment(TextAlignment.START);

            if (builder.title == null) {
                if (titleFrame != null) {
                    titleFrame.setVisibility(Component.HIDE);
                }
            } else {
                tvTitle.setText(builder.title.toString());
                if (titleFrame != null) {
                    titleFrame.setVisibility(Component.VISIBLE);
                }
            }
        }

        if (tvContent != null) {
            tvContent.setFont(builder.regularFont);
            tvContent.setLineSpacing(0f, 1.2f);
            tvContent.setTextColor(new Color(ResTUtil.getColor(builder.getContext(), ResourceTable.Color_md_gray)));
            tvContent.setTextAlignment(TextAlignment.START);

            if (builder.content != null) {
                tvContent.setText(builder.content.toString());
                tvContent.setVisibility(Component.VISIBLE);
            } else {
                tvContent.setVisibility(Component.HIDE);
            }
        }
    }

    private void btnListenerMethod() {
        // Setup action buttons
        textAllCaps = ResTUtil.getBoolean(builder.getContext(), ResourceTable.Boolean_textAllCaps);
        if (null != builder.positiveText && btnPositive != null) {
            btnPositive.setTextColor(
                    new Color(ResTUtil.getColor(builder.getContext(), ResourceTable.Color_colorAccent)));
            btnPositive.setFont(builder.mediumFont);
            String text = builder.positiveText.toString() != null ? builder.positiveText.toString() : "";
            if (textAllCaps && !text.equals("")) {
                text = text.toUpperCase(Locale.ROOT);
            }
            btnPositive.setText(text);
            btnPositive.setTextAlignment(TextAlignment.START);
            btnPositive.setTag(DialogTAction.POSITIVE);
            btnPositive.setClickedListener(
                    new Component.ClickedListener() {
                        @Override
                        public void onClick(Component component) {
                            builder.listener.onClick(component);
                            hide();
                            destroy();
                        }
                    });
        }

        if (null != builder.negativeText && btnNegative != null) {
            btnNegative.setFont(builder.mediumFont);
            String text = builder.negativeText.toString() != null ? builder.negativeText.toString() : "";
            if (textAllCaps && !text.equals("")) {
                text = text.toUpperCase(Locale.ROOT);
            }
            btnNegative.setText(text);
            btnNegative.setTextColor(
                    new Color(ResTUtil.getColor(builder.getContext(), ResourceTable.Color_colorAccent)));
            btnNegative.setTextAlignment(TextAlignment.END);
            btnNegative.setTag(DialogTAction.NEGATIVE);
            btnNegative.setClickedListener(this);
        }
    }

    private void setupInputDialog(Builder builder, final ComponentContainer rootView) {
        input = (TextField) rootView.findComponentById(ResourceTable.Id_input);
        if (input == null) {
            return;
        }
        input.setFont(builder.regularFont);
        if (builder.inputPrefill != null) {
            input.setText(builder.inputPrefill.toString());
        }
        setInternalInputCallback();
        input.setHint(builder.inputHint.toString());
        input.setMaxTextLines(1);
        input.setTextColor(new Color(ResTUtil.getColor(builder.getContext(), ResourceTable.Color_material_black)));
        if (builder.inputType != -1) {
            input.setTextInputType(builder.inputType);
        }
        ShapeElement shapeElement = new ShapeElement();
        shapeElement.setShape(ShapeElement.LINE);
        shapeElement.setStroke(
                5, RgbColor.fromArgbInt(ResTUtil.getColor(builder.getContext(), ResourceTable.Color_colorAccent)));
        input.setBasement(shapeElement);

        tvMinMax = (Text) rootView.findComponentById(ResourceTable.Id_minMax);
        if (builder.inputMinLength > 0 || builder.inputMaxLength > -1) {
            invalidateInputMinMaxIndicator(input.getText().length());
        } else {
            tvMinMax.setVisibility(Component.HIDE);
            tvMinMax = null;
        }
    }

    @Override
    public void hide() {
        super.hide();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    @Override
    public void onClick(Component component) {
        hide();
        destroy();
    }

    @Override
    public boolean onItemSelected(
            DexterCustomDialog dialog, Component itemView, int position, CharSequence text, boolean longPress) {
        if (builder.autoDismiss) {
            // If auto dismiss is enabled, dismiss the dialog when a list item is selected
            hide();
        }
        if (!longPress && builder.listCallback != null) {
            builder.listCallback.onSelection(this, itemView, position, builder.items.get(position));
        }
        return true;
    }

    /**
     * builder模式
     */
    public static class Builder {
        private Context context;
        InputCallback inputCallback;
        ArrayList<CharSequence> items;
        BaseItemProvider baseItemProvider;
        CharSequence inputPrefill;
        CharSequence inputHint;
        CharSequence positiveText;
        CharSequence neutralText;
        CharSequence negativeText;
        CharSequence title;
        CharSequence content;
        Element icon;
        Font regularFont;
        Font mediumFont;
        ListCallback listCallback;
        OnPositiveListener listener;
        int inputType = -1;
        int inputMinLength = -1;
        int inputMaxLength = -1;
        int inputRangeErrorColor = 0;
        boolean inputAllowEmpty;
        boolean autoDismiss = true;

        public Builder(Context context) {
            this.context = context;

            if (this.mediumFont == null) {
                try {
                    this.mediumFont = new Font.Builder("sans-serif-medium").build();
                    this.mediumFont = Font.DEFAULT;
                } catch (Throwable ignored) {
                    this.mediumFont = Font.DEFAULT_BOLD;
                }
            }
            if (this.regularFont == null) {
                try {
                    this.mediumFont = new Font.Builder("sans-serif").build();
                    this.mediumFont = Font.DEFAULT;
                } catch (Throwable ignored) {
                    this.regularFont = Font.SANS_SERIF;
                    if (this.regularFont == null) {
                        this.regularFont = Font.DEFAULT;
                    }
                }
            }
        }

        /**
         * 获取上下文
         *
         * @return Context
         */
        public final Context getContext() {
            return context;
        }

        /**
         * 设置title
         *
         * @param titleRes titleRes
         * @return Builder
         */
        public DexterCustomDialog.Builder title(String titleRes) {
            this.title = titleRes;
            return this;
        }

        /**
         * 设置icon
         *
         * @param iconRes iconRes
         * @return Builder
         */
        public DexterCustomDialog.Builder icon(int iconRes) {
            this.icon = ResTUtil.getPixelMapDrawable(context, iconRes);
            return this;
        }

        /**
         * 设置内容
         *
         * @param contentRes contentRes
         * @return Builder
         */
        public DexterCustomDialog.Builder content(String contentRes) {
            this.content = contentRes;
            return this;
        }

        /**
         * 设置item
         *
         * @param items items
         * @return Builder
         */
        public DexterCustomDialog.Builder items(CharSequence... items) {
            DexterLogUtil.info(TAG, "Entering Collections items");
            this.items = new ArrayList<>();
            Collections.addAll(this.items, items);
            return this;
        }

        /**
         * 设置item点击回调
         *
         * @param callback callback
         * @return Builder
         */
        public DexterCustomDialog.Builder itemsCallback(DexterCustomDialog.ListCallback callback) {
            DexterLogUtil.info(TAG, "Entering itemsCallback");
            this.listCallback = callback;
            return this;
        }

        /**
         * 确定回调
         *
         * @param positiveRes positiveRes
         * @param listener    listener
         * @return Builder
         */
        public DexterCustomDialog.Builder positiveText(String positiveRes, OnPositiveListener listener) {
            if (positiveRes == null) {
                return this;
            }
            this.positiveText = positiveRes;
            this.listener = listener;
            return this;
        }

        /**
         * 设置取消按钮
         *
         * @param id id
         * @return Builder
         */
        public DexterCustomDialog.Builder negativeText(String id) {
            this.negativeText = id;
            return this;
        }

        /**
         * 输入类型
         *
         * @return Builder
         */
        public DexterCustomDialog.Builder inputType() {
            DexterLogUtil.info(TAG, "Entering inputType");
            this.inputType = InputAttribute.PATTERN_TEXT;
            return this;
        }

        /**
         * 输入行数
         *
         * @return Builder
         */
        public DexterCustomDialog.Builder inputRange() {
            DexterLogUtil.info(TAG, "Entering inputRange");
            this.inputMinLength = 2;
            this.inputMaxLength = 16;
            this.inputRangeErrorColor = ResTUtil.getColor(context, ResourceTable.Color_t_edittext_error);
            if (this.inputMinLength > 0) {
                this.inputAllowEmpty = false;
            }
            return this;
        }

        /**
         * 设置input类型数据
         *
         * @param hint            hint
         * @param prefill         prefill
         * @param allowEmptyInput allowEmptyInput
         * @param callback        callback
         * @return Builder
         */
        public DexterCustomDialog.Builder input(
                int hint, int prefill, boolean allowEmptyInput, InputCallback callback) {
            DexterLogUtil.info(TAG, "Entering input3");
            return input(
                    hint == 0 ? null : ResTUtil.getString(context, hint),
                    prefill == 0 ? null : ResTUtil.getString(context, prefill),
                    allowEmptyInput,
                    callback);
        }

        /**
         * 设置input类型数据
         *
         * @param hint            hint
         * @param prefill         prefill
         * @param allowEmptyInput allowEmptyInput
         * @param callback        callback
         * @return Builder
         */
        DexterCustomDialog.Builder input(
                CharSequence hint, CharSequence prefill, boolean allowEmptyInput, InputCallback callback) {
            DexterLogUtil.info(TAG, "Entering input");
            this.inputCallback = callback;
            this.inputHint = hint;
            this.inputPrefill = prefill;
            this.inputAllowEmpty = allowEmptyInput;
            return this;
        }

        /**
         * 展示
         */
        public void show() {
            new DexterCustomDialog(this).show();
        }
    }

    /**
     * 列表监听
     */
    public interface ListCallback {
        /**
         * list点击监听
         *
         * @param dialog   dialog
         * @param itemView itemView
         * @param position position
         * @param text     text
         */
        void onSelection(DexterCustomDialog dialog, Component itemView, int position, CharSequence text);
    }

    /**
     * 设置输入回调监听
     */
    public interface InputCallback {
        void onInput(DexterCustomDialog dialog, CharSequence input);
    }

    private int getInflateLayout(DexterCustomDialog.Builder builder) {
        DexterLogUtil.info(TAG, "Entering getInflateLayout");
        if (builder.inputCallback != null) {
            return ResourceTable.Layout_uilayouts_dependent_layout;
        } else if (builder.items != null || builder.baseItemProvider != null) {
            return ResourceTable.Layout_uilayouts_stack_layout;
        }
        return ResourceTable.Layout_uilayouts_directional_layout;
    }

    private void invalidateList() {
        DexterLogUtil.info(TAG, "Entering invalidateList");
        if (listContainer == null) {
            return;
        } else if ((builder.items == null || builder.items.size() == 0) && builder.baseItemProvider == null) {
            return;
        }
        listContainer.setItemProvider(builder.baseItemProvider);
        ((ListContainerAdapter) builder.baseItemProvider).setCallback(this);
    }

    private void setInternalInputCallback() {
        if (input == null) {
            return;
        }
        input.addTextObserver(
                (sa, pos, i1, i2) -> {
                    final int length = sa.length();
                    invalidateInputMinMaxIndicator(length);
                    builder.inputCallback.onInput(DexterCustomDialog.this, sa);
                });
    }

    private void invalidateInputMinMaxIndicator(int currentLength) {
        DexterLogUtil.info(TAG, "Entering invalidateInputMinMaxIndicator");
        if (tvMinMax != null) {
            if (builder.inputMaxLength > 0) {
                tvMinMax.setText(String.format(Locale.getDefault(), "%d/%d", currentLength, builder.inputMaxLength));
                tvMinMax.setVisibility(Component.VISIBLE);
            } else {
                tvMinMax.setVisibility(Component.HIDE);
            }
            if (builder.inputMaxLength > 0) {
                tvMinMax.setTextColor(
                        new Color(ResTUtil.getColor(builder.getContext(), ResourceTable.Color_textColorSecondary)));
            }
        }
    }

    /**
     * Positive监听
     */
    public interface OnPositiveListener {
        /**
         * onClick component
         *
         * @param component component
         */
        void onClick(Component component);
    }
}
